// Copyright 1998-2017 Epic Games, Inc. All Rights Reserved.

/*=============================================================================
UnrealTypeTraits.h: Unreal type traits definitions.
Note: Boost does a much better job of limiting instantiations.
We require VC8 so a lot of potential version checks are omitted.
=============================================================================*/

#pragma once

#include "../BasicTypes.h"
#include "IsPointer.h"
#include "AndOrNot.h"
#include "AreTypesEqual.h"
#include "IsArithmetic.h"
#include "IsEnum.h"
#include "RemoveCV.h"

#include "IsPODType.h"
#include "IsTriviallyCopyConstructible.h"


/*-----------------------------------------------------------------------------
* Macros to abstract the presence of certain compiler intrinsic type traits
-----------------------------------------------------------------------------*/
#define HAS_TRIVIAL_CONSTRUCTOR(T) __has_trivial_constructor(T)
#define IS_POD(T) __is_pod(T)
#define IS_EMPTY(T) __is_empty(T)


/*-----------------------------------------------------------------------------
Type traits similar to TR1 (uses intrinsics supported by VC8)
Should be updated/revisited/discarded when compiler support for tr1 catches up.
-----------------------------------------------------------------------------*/

/**
* TIsSame
*
* Unreal implementation of std::is_same trait.
*/
template<typename A, typename B>	struct TIsSame { enum { Value = false }; };
template<typename T>				struct TIsSame<T, T> { enum { Value = true }; };

/**
* TIsCharType
*/
template<typename T> struct TIsCharType { enum { Value = false }; };
template<>           struct TIsCharType<ANSICHAR> { enum { Value = true }; };
template<>           struct TIsCharType<UCS2CHAR> { enum { Value = true }; };
template<>           struct TIsCharType<WIDECHAR> { enum { Value = true }; };

/**
* TFormatSpecifier, only applies to numeric types
*/
template<typename T>
struct TFormatSpecifier
{
	FORCEINLINE static TCHAR const* GetFormatSpecifier()
	{
		// Force the template instantiation to be dependent upon T so the compiler cannot automatically decide that this template can never be instantiated.
		// If the static_assert below were a constant 0 or something not dependent on T, compilers are free to detect this and fail to compile the template.
		// As specified in the C++ standard s14.6p8. A compiler is free to give a diagnostic here or not. MSVC ignores it, and clang/gcc instantiates the 
		// template and triggers the static_assert.
		static_assert(sizeof(T) < 0, "Format specifier not supported for this type."); // request for a format specifier for a type we do not know about
		return TEXT("Unknown");
	}
};
#define Expose_TFormatSpecifier(type, format) \
template<> \
struct TFormatSpecifier<type> \
{  \
	FORCEINLINE static const TCHAR (&GetFormatSpecifier())[5] \
	{ \
		static const TCHAR Spec[5] = TEXT(format); \
		return Spec; \
	} \
};

Expose_TFormatSpecifier(bool, "%i")
Expose_TFormatSpecifier(uint8, "%u")
Expose_TFormatSpecifier(uint16, "%u")
Expose_TFormatSpecifier(uint32, "%u")
Expose_TFormatSpecifier(uint64, "%llu")
Expose_TFormatSpecifier(int8, "%d")
Expose_TFormatSpecifier(int16, "%d")
Expose_TFormatSpecifier(int32, "%d")
Expose_TFormatSpecifier(int64, "%lld")
Expose_TFormatSpecifier(float, "%f")
Expose_TFormatSpecifier(double, "%f")
Expose_TFormatSpecifier(long double, "%f")


/**
* TIsReferenceType
*/
template<typename T> struct TIsReferenceType { enum { Value = false }; };
template<typename T> struct TIsReferenceType<T&> { enum { Value = true }; };
template<typename T> struct TIsReferenceType<T&&> { enum { Value = true }; };

/**
* TIsLValueReferenceType
*/
template<typename T> struct TIsLValueReferenceType { enum { Value = false }; };
template<typename T> struct TIsLValueReferenceType<T&> { enum { Value = true }; };

/**
* TIsRValueReferenceType
*/
template<typename T> struct TIsRValueReferenceType { enum { Value = false }; };
template<typename T> struct TIsRValueReferenceType<T&&> { enum { Value = true }; };

/**
* TIsVoidType
*/
template<typename T> struct TIsVoidType { enum { Value = false }; };
template<> struct TIsVoidType<void> { enum { Value = true }; };
template<> struct TIsVoidType<void const> { enum { Value = true }; };
template<> struct TIsVoidType<void volatile> { enum { Value = true }; };
template<> struct TIsVoidType<void const volatile> { enum { Value = true }; };

/**
* TIsFundamentalType
*/
template<typename T>
struct TIsFundamentalType
{
	enum { Value = TOr<TIsArithmetic<T>, TIsVoidType<T>>::Value };
};

/**
* TIsFunction
*
* Tests is a type is a function.
*/
template <typename T>
struct TIsFunction
{
	enum { Value = false };
};

template <typename RetType, typename... Params>
struct TIsFunction<RetType(Params...)>
{
	enum { Value = true };
};

/**
* TIsZeroConstructType
*/
template<typename T>
struct TIsZeroConstructType
{
	enum { Value = TOr<TIsEnum<T>, TIsArithmetic<T>, TIsPointer<T>>::Value };
};

/**
* TIsWeakPointerType
*/
template<typename T>
struct TIsWeakPointerType
{
	enum { Value = false };
};


/**
* TNameOf
*/
template<typename T>
struct TNameOf
{
	FORCEINLINE static TCHAR const* GetName()
	{
		return TEXT("Unknown");
	}
};

#define Expose_TNameOf(type) \
template<> \
struct TNameOf<type> \
{  \
	FORCEINLINE static TCHAR const* GetName() \
	{ \
		return TEXT(#type); \
	} \
};

Expose_TNameOf(uint8)
Expose_TNameOf(uint16)
Expose_TNameOf(uint32)
Expose_TNameOf(uint64)
Expose_TNameOf(int8)
Expose_TNameOf(int16)
Expose_TNameOf(int32)
Expose_TNameOf(int64)
Expose_TNameOf(float)
Expose_TNameOf(double)

/*-----------------------------------------------------------------------------
Call traits - Modeled somewhat after boost's interfaces.
-----------------------------------------------------------------------------*/

/**
* Call traits helpers
*/
template <typename T, bool TypeIsSmall>
struct TCallTraitsParamTypeHelper
{
	typedef const T& ParamType;
	typedef const T& ConstParamType;
};
template <typename T>
struct TCallTraitsParamTypeHelper<T, true>
{
	typedef const T ParamType;
	typedef const T ConstParamType;
};
template <typename T>
struct TCallTraitsParamTypeHelper<T*, true>
{
	typedef T* ParamType;
	typedef const T* ConstParamType;
};


/*-----------------------------------------------------------------------------
Helper templates for dealing with 'const' in template code
-----------------------------------------------------------------------------*/

/**
* TRemoveConst<> is modeled after boost's implementation.  It allows you to take a templatized type
* such as 'const Foo*' and use const_cast to convert that type to 'Foo*' without knowing about Foo.
*
*		MutablePtr = const_cast< RemoveConst< ConstPtrType >::Type >( ConstPtr );
*/
template< class T >
struct TRemoveConst
{
	typedef T Type;
};
template< class T >
struct TRemoveConst<const T>
{
	typedef T Type;
};


/*-----------------------------------------------------------------------------
* TCallTraits
*
* Same call traits as boost, though not with as complete a solution.
*
* The main member to note is ParamType, which specifies the optimal
* form to pass the type as a parameter to a function.
*
* Has a small-value optimization when a type is a POD type and as small as a pointer.
-----------------------------------------------------------------------------*/

/**
* base class for call traits. Used to more easily refine portions when specializing
*/
template <typename T>
struct TCallTraitsBase
{
private:
	enum { PassByValue = TOr<TAndValue<(sizeof(T) <= sizeof(void*)), TIsPODType<T>>, TIsArithmetic<T>, TIsPointer<T>>::Value };
public:
	typedef T ValueType;
	typedef T& Reference;
	typedef const T& ConstReference;
	typedef typename TCallTraitsParamTypeHelper<T, PassByValue>::ParamType ParamType;
	typedef typename TCallTraitsParamTypeHelper<T, PassByValue>::ConstParamType ConstPointerType;
};

/**
* TCallTraits
*/
template <typename T>
struct TCallTraits : public TCallTraitsBase<T> {};

// Fix reference-to-reference problems.
template <typename T>
struct TCallTraits<T&>
{
	typedef T& ValueType;
	typedef T& Reference;
	typedef const T& ConstReference;
	typedef T& ParamType;
	typedef T& ConstPointerType;
};

// Array types
template <typename T, size_t N>
struct TCallTraits<T[N]>
{
private:
	typedef T ArrayType[N];
public:
	typedef const T* ValueType;
	typedef ArrayType& Reference;
	typedef const ArrayType& ConstReference;
	typedef const T* const ParamType;
	typedef const T* const ConstPointerType;
};

// const array types
template <typename T, size_t N>
struct TCallTraits<const T[N]>
{
private:
	typedef const T ArrayType[N];
public:
	typedef const T* ValueType;
	typedef ArrayType& Reference;
	typedef const ArrayType& ConstReference;
	typedef const T* const ParamType;
	typedef const T* const ConstPointerType;
};


/*-----------------------------------------------------------------------------
Traits for our particular container classes
-----------------------------------------------------------------------------*/

/**
* Helper for array traits. Provides a common base to more easily refine a portion of the traits
* when specializing. Mainly used by MemoryOps.h which is used by the contiguous storage containers like TArray.
*/
template<typename T>
struct TTypeTraitsBase
{
	typedef typename TCallTraits<T>::ParamType ConstInitType;
	typedef typename TCallTraits<T>::ConstPointerType ConstPointerType;

	// There's no good way of detecting this so we'll just assume it to be true for certain known types and expect
	// users to customize it for their custom types.
	enum { IsBytewiseComparable = TOr<TIsEnum<T>, TIsArithmetic<T>, TIsPointer<T>>::Value };
};

/**
* Traits for types.
*/
template<typename T> struct TTypeTraits : public TTypeTraitsBase<T> {};


/**
* Traits for containers.
*/
template<typename T> struct TContainerTraitsBase
{
	// This should be overridden by every container that supports emptying its contents via a move operation.
	enum { MoveWillEmptyContainer = false };
};

template<typename T> struct TContainerTraits : public TContainerTraitsBase<T> {};

struct FVirtualDestructor
{
	virtual ~FVirtualDestructor() {}
};

template <typename T, typename U>
struct TMoveSupportTraitsBase
{
	// Param type is not an const lvalue reference, which means it's pass-by-value, so we should just provide a single type for copying.
	// Move overloads will be ignored due to SFINAE.
	typedef U Copy;
};

template <typename T>
struct TMoveSupportTraitsBase<T, const T&>
{
	// Param type is a const lvalue reference, so we can provide an overload for moving.
	typedef const T& Copy;
	typedef T&&      Move;
};

/**
* This traits class is intended to be used in pairs to allow efficient and correct move-aware overloads for generic types.
* For example:
*
* template <typename T>
* void Func(typename TMoveSupportTraits<T>::Copy Obj)
* {
*     // Copy Obj here
* }
*
* template <typename T>
* void Func(typename TMoveSupportTraits<T>::Move Obj)
* {
*     // Move from Obj here as if it was passed as T&&
* }
*
* Structuring things in this way will handle T being a pass-by-value type (e.g. ints, floats, other 'small' types) which
* should never have a reference overload.
*/
template <typename T>
struct TMoveSupportTraits : TMoveSupportTraitsBase<T, typename TCallTraits<T>::ParamType>
{
};

/**
* Tests if a type T is bitwise-constructible from a given argument type U.  That is, whether or not
* the U can be memcpy'd in order to produce an instance of T, rather than having to go
* via a constructor.
*
* Examples:
* TIsBitwiseConstructible<PODType,    PODType   >::Value == true  // PODs can be trivially copied
* TIsBitwiseConstructible<const int*, int*      >::Value == true  // a non-const Derived pointer is trivially copyable as a const Base pointer
* TIsBitwiseConstructible<int*,       const int*>::Value == false // not legal the other way because it would be a const-correctness violation
* TIsBitwiseConstructible<int32,      uint32    >::Value == true  // signed integers can be memcpy'd as unsigned integers
* TIsBitwiseConstructible<uint32,     int32     >::Value == true  // and vice versa
*/

template <typename T, typename Arg>
struct TIsBitwiseConstructible
{
	static_assert(
		!TIsReferenceType<T  >::Value &&
		!TIsReferenceType<Arg>::Value,
		"TIsBitwiseConstructible is not designed to accept reference types");

	static_assert(
		TAreTypesEqual<T, typename TRemoveCV<T  >::Type>::Value &&
		TAreTypesEqual<Arg, typename TRemoveCV<Arg>::Type>::Value,
		"TIsBitwiseConstructible is not designed to accept qualified types");

	// Assume no bitwise construction in general
	enum { Value = false };
};

template <typename T>
struct TIsBitwiseConstructible<T, T>
{
	// Ts can always be bitwise constructed from itself if it is trivially copyable.
	enum { Value = TIsTriviallyCopyConstructible<T>::Value };
};

template <typename T, typename U>
struct TIsBitwiseConstructible<const T, U> : TIsBitwiseConstructible<T, U>
{
	// Constructing a const T is the same as constructing a T
};

// Const pointers can be bitwise constructed from non-const pointers.
// This is not true for pointer conversions in general, e.g. where an offset may need to be applied in the case
// of multiple inheritance, but there is no way of detecting that at compile-time.
template <typename T>
struct TIsBitwiseConstructible<const T*, T*>
{
	// Constructing a const T is the same as constructing a T
	enum { Value = true };
};

// Unsigned types can be bitwise converted to their signed equivalents, and vice versa.
// (assuming two's-complement, which we are)
template <> struct TIsBitwiseConstructible<uint8, int8> { enum { Value = true }; };
template <> struct TIsBitwiseConstructible< int8, uint8> { enum { Value = true }; };
template <> struct TIsBitwiseConstructible<uint16, int16> { enum { Value = true }; };
template <> struct TIsBitwiseConstructible< int16, uint16> { enum { Value = true }; };
template <> struct TIsBitwiseConstructible<uint32, int32> { enum { Value = true }; };
template <> struct TIsBitwiseConstructible< int32, uint32> { enum { Value = true }; };
template <> struct TIsBitwiseConstructible<uint64, int64> { enum { Value = true }; };
template <> struct TIsBitwiseConstructible< int64, uint64> { enum { Value = true }; };

#define GENERATE_MEMBER_FUNCTION_CHECK(MemberName, Result, ConstModifier, ...)									\
template <typename T>																							\
class THasMemberFunction_##MemberName																			\
{																												\
	template <typename U, Result(U::*)(__VA_ARGS__) ConstModifier> struct Check;								\
	template <typename U> static char MemberTest(Check<U, &U::MemberName> *);									\
	template <typename U> static int MemberTest(...);															\
public:																											\
	enum { Value = sizeof(MemberTest<T>(nullptr)) == sizeof(char) };											\
};

/*-----------------------------------------------------------------------------
* Undef Macros abstracting the presence of certain compiler intrinsic type traits
-----------------------------------------------------------------------------*/
#undef IS_EMPTY
#undef IS_POD
#undef HAS_TRIVIAL_CONSTRUCTOR
